package com.gopay.bis.service.bank.impl;

import java.math.BigDecimal;
import java.util.Map;

import javax.annotation.Resource;

import com.gopay.common.cipher.utils.LoggerMaskUtils;
import com.gopay.remote.cashier.bis.CashierBgNotifyRemoteService;
import com.gopay.remote.cps.bis.ErrOrderRecordRemoteService;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;

import com.gopay.bis.manager.bank.BankOrderManager;
import com.gopay.bis.service.bank.BankIntrChannConfService;
import com.gopay.bis.service.bank.BankIntrChannConfService.OutStlmTranCode;
import com.gopay.bis.service.bank.BankOrderService;
import com.gopay.bis.service.bank.go.GoBankService;
import com.gopay.bis.service.bank.query.BisBankWhiteListMerchantsService;
import com.gopay.bis.service.bank.task.ExPayTaskThread;
import com.gopay.common.constants.bank.BankConstants;
import com.gopay.common.constants.bank.BisBankBusinessProd;
import com.gopay.common.constants.bank.BisBankInterfaceChannel;
import com.gopay.common.constants.bank.ExPayBankConstants;
import com.gopay.common.constants.proccode.ProcCodeConstants;
import com.gopay.common.constants.txncd.IntTxnCd;
import com.gopay.common.cps.manager.order.CpsGeneralMainOrderQueryManager;
import com.gopay.common.domain.bank.BaseMessage;
import com.gopay.common.domain.bank.ExPayBaseMessage;
import com.gopay.common.domain.bank.PayRequest;
import com.gopay.common.domain.bank.PayResult;
import com.gopay.common.domain.bis.BaseBisBankConfig;
import com.gopay.common.domain.bis.BaseBisBankExtConfig;
import com.gopay.common.domain.bis.RouteMessage;
import com.gopay.common.domain.proccode.ProcCodeInfo;
import com.gopay.common.exception.GopayException;
import com.gopay.common.order.bankorder.trade.InitialSpringBeanName;
import com.gopay.common.proccode.DicProcCodeService;
import com.gopay.common.util.HttpTransport;
import com.gopay.remote.bank.BisBaseExPayInterfaceRemoteService;
import com.gopay.remote.bank.ExPayInteractRemoteService;
import com.gopay.remote.bank.autoroute.BankRoute;

@Service("bisExPayService")
public class BisBaseExPayServiceImpl implements BisBaseExPayInterfaceRemoteService {

    private static Logger logger = LoggerFactory.getLogger(BisBaseExPayServiceImpl.class);
    private static final String AUTO_ROUTING = "on"; // 银行自动路由开关

    @Resource(name = "goBankService")
    private GoBankService goBankService;

    @Resource(name = "bankIntrChannConfService")
    private BankIntrChannConfService bankIntrChannConfService;

    @Resource(name = "dicProcCodeService")
    private DicProcCodeService dicProcCodeService;

    @Resource(name = "notifyCashier")
    private HttpTransport notifyCashier;

    @Resource(name = "cashierBgNotifyRemoteService")
    private CashierBgNotifyRemoteService cashierBgNotifyRemoteService;

    @Resource(name = "bisConfig")
    protected Map<String, String> bisConfig;

    @Resource(name = "bankOrderManager")
    private BankOrderManager bankOrderManager;

    @Resource(name = "cpsGeneralMainOrderQueryManager")
    private CpsGeneralMainOrderQueryManager mainOrderQuery;

    @Resource(name = "bankOrderService")
    private BankOrderService bankOrderService;

    @Autowired
    @Qualifier("taskExecutor")
    private TaskExecutor taskExecutor;

    public void setTaskExecutor(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }
    // @Resource(name = "mtPaybankInteractService")
    // private MtPayInteractRemoteService mtPaybankInteractService;

    @Resource(name = "bankRouteService")
    private BankRoute bankRouteService;

    @Resource(name = "normalBankRouteService")
    private BankRoute normalBankRouteService;

    @Resource(name = "exPaybankInteractService")
    private ExPayInteractRemoteService exPaybankInteractService;

    @Resource(name = "bisBankWhiteListMerchantsService")
    private BisBankWhiteListMerchantsService bisBankWhiteListMerchantsService;

	@Resource(name = "errOrderRecordService")
	protected ErrOrderRecordRemoteService errOrderRecordService;

    @Override
    public PayResult pay(PayRequest msg) {
        logger.error("--------------- BisBaseExPayServiceImpl pay  ---------------");
        // 打印请求参数
        PayRequest logData = getPrintMsg(msg); // 日志输出隐藏信用卡cvv2
        logger.error("收到的请求参数键:" + LoggerMaskUtils.maskString(ToStringBuilder.reflectionToString(logData)));
        PayResult payPesult = new PayResult(true);
        try {

            checkRequest(msg, payPesult);

            // 订单号
            String orderId = msg.getOrderId();
            // 银行业务产品
            String bankBusProd = msg.getBankBusProd();
            // 手机号
            String phoneNo = msg.getPhoneNo();
            // 持卡人姓名
            String cardHolder = msg.getCardHolder();
            // 卡号
            String cardNo = msg.getCardNo();
            String validateCode = msg.getValidateCode();
            String cvv2 = msg.getCvv2();
            String availableDate = msg.getAvailableDate();
            String bank = msg.getBank();
            String cidNo = msg.getCidNo();
            String cidType = msg.getCidType();
            BisBankBusinessProd bankBusProdEnum = BisBankBusinessProd.get(bankBusProd);
            // 持卡人IP地址
            String ip = msg.getIp();
            // 消息对象
            BaseMessage base = goBankService.buildSign(orderId, ip);
            base.setBank(bank);
            base.setBankBusProd(bankBusProdEnum);

            BaseBisBankExtConfig bankConfig = new BaseBisBankExtConfig();
//            boolean isbankRoute = false; // 是否自动路由标识
//            if (!"3".equals(msg.getSpecialChCode())) {
//                // 基金交易跳过自动路由
//                // 是否为白名单商户
//                isbankRoute = isRouteOrWhiteList(base.getSpecial2());
//            }

            if (isRouteOrWhiteList(base.getSpecial2())) {
                // 增加银行自动路由开关 add by zhengxue
                RouteMessage routeMessage = new RouteMessage();
                routeMessage.setCustId(base.getSpecial2());
                routeMessage.setGopayOrderId(base.getOrderId());
                routeMessage.setBankCode(base.getBank());
                routeMessage.setProdCode(bankBusProd);
                // 自动路由交易金额获取接口传送的结算金额
                // 金额比对
                if (msg.getSettleAmt() != null && new BigDecimal(msg.getSettleAmt()).compareTo(base.getAmount()) != 0) {
                    // 接口传送金额与数据库金额不符
                    logger.error(base.getOrderId() + " 快捷支付接口传送金额与数据库金额不符!");
                    payPesult.setSuccess(false);
                    payPesult.setResultCode(ProcCodeConstants.PROC_CODE_100E6107);
                    payPesult.setResultMsg("快捷支付接口传送金额与数据库金额不符");
                    return payPesult;
                }
                routeMessage.setTransAmt(new BigDecimal(msg.getSettleAmt()));
                routeMessage.setSpecial("payment");

                if (IntTxnCd._00500.value.equals(base.getSpecial4())
                        || BankConstants.GOPAY_CHONGZHI_CUSTID.equals(base.getSpecial2())) {
                    // 充值交易走通用方案
                    bankConfig = normalBankRouteService.getNormalRoute(routeMessage);
                    if (BankConstants.GOPAY_CHONGZHI_CUSTID.equals(base.getSpecial2())
                            && !IntTxnCd._00500.value.equals(base.getSpecial4())) {
                        // 非充值订单获取merId为GFB0000001
                        logger.error(orderId + " BisBaseExPayServiceImpl支付交易获取商户ID为: " + base.getSpecial2());
                    }
                } else {
                    bankConfig = bankRouteService.getRoute(routeMessage);
                }
                bankOrderService.addBankRouteLog(routeMessage); // 记录银行自动路由日志
            } else {
                // 获取银行接口通道配置信息
                bankConfig = bankIntrChannConfService.findBaseBisBankExtConfig(base.getBankBusProd(), base.getBank());
                if ("1".equals(msg.getSpecialChCode())) {
                    // 钱包三冲交易固定走易宝通道
                    if (!BisBankInterfaceChannel._09.toString().equals(bankConfig.getChCode())) {
                        // 根据银行业务产品代码、通道代码、银行代码获取通道配置信息
                        bankConfig = bankIntrChannConfService.findBaseBisBankExtConfigByBankChannel(
                                base.getBankBusProd(), BisBankInterfaceChannel._09, base.getBank());
                        logger.error("BisBaseExPayServiceImpl pay specialChCode: 1");
                    }
                }
                if ("2".equals(msg.getSpecialChCode())) {
                    // 钱包天宜盈交易固定走民生代扣通道
                    if (!BisBankInterfaceChannel._11.toString().equals(bankConfig.getChCode())) {
                        // 根据银行业务产品代码、通道代码、银行代码获取通道配置信息
                        bankConfig = bankIntrChannConfService.findBaseBisBankExtConfigByBankChannel(
                                base.getBankBusProd(), BisBankInterfaceChannel._11, base.getBank());
                        logger.error("BisBaseExPayServiceImpl pay specialChCode: 2");
                    }
                }
            }

            if (BisBankInterfaceChannel._14.value.equals(bankConfig.getChCode())) {
                // 银行产品空通道
                logger.error(orderId + " BisBaseExPayServiceImpl获取银行产品空通道");
                payPesult.setResultCode(ProcCodeConstants.PROC_CODE_100E6090);
                payPesult.setSuccess(false);
                return payPesult;
            }

            // 校验银行业务产品是否支持该通道
            checkBankBussProdAndIntrChan(bankConfig, bankBusProd);
            // 校验通道信息
            checkBankIntrChan(bankConfig, OutStlmTranCode._1000);
            // 平台外部清算机构代码
            String outStlmCd = InitialSpringBeanName.getOutStlmCd(bankConfig.getChCode(), bankBusProd, base.getBank());
            String intrfaceBankCode = bankConfig.getIntrfaceBankCode();
            if (StringUtils.isBlank(intrfaceBankCode)) {
                intrfaceBankCode = base.getBank();
            }
            base.setBankIntrChann(BisBankInterfaceChannel.get(bankConfig.getChCode()));
            base.setSpecial(ip);
            base.setOutStlmCd(outStlmCd);
            ExPayBaseMessage exPayMsg = new ExPayBaseMessage();
            // 设置快捷支付信息
            BeanUtils.copyProperties(base, exPayMsg);
            exPayMsg.setMediaId(phoneNo);
            exPayMsg.setCardHolder(cardHolder);
            exPayMsg.setCardNo(cardNo);
            if (validateCode != null) {
                exPayMsg.setValidateCode(validateCode);
            }
            if ((BisBankBusinessProd._05.value).equals(bankBusProd)) {
                // 信用卡产品操作有效期
                exPayMsg.setCvv2(cvv2);
                exPayMsg.setAvailableDate(availableDate);
            }
            
            exPayMsg.setIdentityType("IDENTITY_CARD");
            exPayMsg.setIdentityCode(cidNo);
            exPayMsg.setTermination(msg.getTermination());
            exPayMsg.setUserIp(msg.getIp());
            // 三冲、天宜盈、基金交易渠道特殊处理码
            exPayMsg.setSpecialChCode(msg.getSpecialChCode());
            if (ExPayBankConstants.TERMINAL_TYPE_PC.equals(msg.getTermination())) {
                exPayMsg.setBrowser(msg.getBrowser());
            }
            
            if (ExPayBankConstants.TERMINAL_TYPE_PHONE.equals(msg.getTermination())) {
                exPayMsg.setImei(msg.getImei());
            }

			pay(exPayMsg);
		} catch (GopayException e) {
			logger.error("BisBaseExPayServiceImpl GopayException", e);
			ProcCodeInfo procCodeInfo = dicProcCodeService.getProcCodeInfo(e
					.getErrCode());
			payPesult.setSuccess(false);
			payPesult.setResultCode(e.getErrCode());
			payPesult.setResultMsg(procCodeInfo.getMessage());
		} catch (Exception e) {
			logger.error("BisBaseExPayServiceImpl Exception", e);
			payPesult.setSuccess(false);
			payPesult.setResultCode(ProcCodeConstants.PROC_CODE_100F1002);
			payPesult.setResultMsg(e.getMessage());
		}
		return payPesult;
	}
	
	private void pay(ExPayBaseMessage exPayMsg){
		ExPayTaskThread thread = new ExPayTaskThread(exPayMsg,exPaybankInteractService,errOrderRecordService,cashierBgNotifyRemoteService);
		thread.setNotifyCashier(notifyCashier);
		thread.setBankOrderManager(bankOrderManager);
		thread.setMainOrderQuery(mainOrderQuery);
		taskExecutor.execute(thread);
	}

    /**
     * 校验银行通道信息
     * 
     * @param BaseBisBankConfig
     * @param OutStlmTranCode
     * @throws GopayException
     */
    private void checkBankIntrChan(BaseBisBankConfig config, OutStlmTranCode outTranCode) throws GopayException {
        bankIntrChannConfService.checkBankIntrChan(config, outTranCode);
    }

    /**
     * 校验银行业务产品是否支持该通道
     * 
     * @param config
     * @param bankBusProdEnum
     * @throws GopayException
     */
    private void checkBankBussProdAndIntrChan(BaseBisBankExtConfig config, String bankBusProd) throws GopayException {
        if (null == config) {
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E7021);
        }
        String intrChann = config.getChCode();// 接口通道
        bankIntrChannConfService.checkBankBussProdAndIntrChan(intrChann, bankBusProd);
    }

    /**
     * 
     * @param HttpServletRequest
     * @throws GopayException
     * 
     */
    private void checkRequest(PayRequest request, PayResult payPesult) throws GopayException {
        // 订单号
        String orderId = request.getOrderId();
        // 银行业务产品
        String bankBusProd = request.getBankBusProd();

        // 手机号
        String phoneNo = request.getPhoneNo();
        // 持卡人姓名
        String cardHolder = request.getCardHolder();
        // 卡号
        String cardNo = request.getCardNo();
        String cvv2 = request.getCvv2();
        String availableDate = request.getAvailableDate();
        String bank = request.getBank();
        String cidNo = request.getCidNo();
        String cidType = request.getCidType();
        if (StringUtils.isBlank(orderId)) {
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E1089);
        }
        if (StringUtils.isBlank(bankBusProd)) {
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E7014);
        }
        if (StringUtils.isBlank(phoneNo)) {
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E6073);
        }
        if (StringUtils.isBlank(cardHolder)) {
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E6072);
        }
        if (StringUtils.isBlank(cardNo)) {
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E6069);
        }
        if ((BisBankBusinessProd._05.value).equals(bankBusProd)) {
            // 信用卡校验cvv2和有效期
            // if (StringUtils.isBlank(cvv2)) {
            // throw new GopayException(ProcCodeConstants.PROC_CODE_100E6070);
            // }
            // if (StringUtils.isBlank(availableDate)) {
            // throw new GopayException(ProcCodeConstants.PROC_CODE_100E6071);
            // }
        }
        if (StringUtils.isBlank(bank)) {
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E1135);
        }
        if (StringUtils.isBlank(cidNo)) {
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E6075);
        }
        if (StringUtils.isBlank(cidType)) {
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E6074);
        }
    }

    /**
     * 快捷对象输出，隐藏信用卡cvv2
     * 
     * @param baseMsg
     * @return
     */
    private PayRequest getPrintMsg(PayRequest baseMsg) {
        PayRequest logData = new PayRequest();
        BeanUtils.copyProperties(baseMsg, logData);
        if (logData.getCvv2() == null || "".equals(logData.getCvv2())) {
            //
        } else {
            logData.setCvv2("***");
        }
        return logData;
    }

    /**
     * 是否为自动路由
     * 
     * @param merId
     * @return
     */
    private boolean isRouteOrWhiteList(String merId) {
        // 是否为白名单商户
        boolean isWhite = bisBankWhiteListMerchantsService.isWhiteListMerchant(merId);
        if (AUTO_ROUTING.equals(bisConfig.get("GOPAY.AUTO.ROUTING")) || isWhite) {
            return true;
        } else {
            return false;
        }
    }

}
